/* Networking with client by packets implemented here */
#include "amever.h"
#include "packets.h"

static NetPackets NP[] = {
		{ PacketChatSystem, '0', '0', 2, 2, netParserFinishPacket },
		{ PacketChatNotice, '0', '1', 2, 2, netParserFinishPacket },
		{ PacketChat, '0', '2', 2, 2, netParserClientChat },
		{ PacketChatShout, '0', '3', 2, 2, netParserClientChat },
		{ PacketChatPM, '0', '4', 3, 2, netParserClientPMChat },
		{ PacketChatTrade, '0', '5', 2, 2, netParserClientChat },
		{ PacketChatParty, '0', '6', 2, 2, netParserClientChat },
		{ PacketChatGuild, '0', '7', 2, 2, netParserClientChat },
		EMPTY_PACKET, EMPTY_PACKET,
		{ PacketAuthRequest, '1', '0', 2, 2, netParserClientLogin },
		{ PacketAuthSuccess, '1', '1', 0, 2, netParserFinishPacket },
		{ PacketAuthDeny, '1', '2', 0, 2, netParserFinishPacket },
		{ PacketKick, '1', '3', 0, 2, netParserLogout },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		EMPTY_PACKET,
		{ PacketObjectLocated, '2', '0', 0, 8, netParserFinishPacket },
		{ PacketObjectMovement, '2', '1', 2, 4, netParserCharMovement },
		{ PacketObjectTrajectoryChange, '2', '2', 2, 4, netParserWhois },
		{ PacketObjectDisappeared, '2', '3', 2, 2, netParserWhois },
		{ PacketLookAroundRequest, '2', '4', 0, 2, netParserCharMapInfo },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		{ PacketCharselectCountRequest, '3', '0', 0, 2, netParserCharselectCount },
		{ PacketCharInfoRequest, '3', '1', 1, 3, netParserCharselectInfo },
		{ PacketCharselectRequest, '3', '2', 1, 4, netParserCharselect },
		{ PacketCharselectRequest, '3', '3', 1, 2, netParserFinishPacket },
		{ PacketCharSelectedOK, '3', '4', 2, 2, netParserFinishPacket },
		{ PacketCharSelectedFailure, '3', '5', 2, 2, netParserFinishPacket },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		{ PacketMoveChar, '4', '0', 2, 2, netParserLookAroundReq },
		{ PacketMoveRequest, '4', '1', 2, 2, netParserCharMovement },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		{ PacketWhois, '5', '0', 2, 4, netParserWhois },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		{ PacketInventoryList, '6', '0', 2, 2, netParserInventoryList },
		{ PacketInventoryInfo, '6', '1', 2, 4, netParserInventoryInfo },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		{ PacketMobLoot, '7', '0', 2, 4, netParserMobLoot },
		{ PacketMobLootGet, '7', '1', 2, 5, netParserMobLootGet },
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, 
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		// 80
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		// 90
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET, EMPTY_PACKET,
		// 100
		{ PacketClientSkill, 'A', '0', 2, 6, netParserSkillPacket },	
		{ PacketUnknown, '9', '9', 2, 2, netParserFinishPacket }
};

int netParser(struct client *me) {
	// It is called allways we have data waiting to be read.
	if (!me || !me->fd) {
		amelog("Something wrong passed to netParser which is not an active client.\n");
		return 1;
	}
	
	/*
	if (!me->logged && RFIFOW(me,0) !=10 && RFIFOW(me, 0)!=0)
	{
		amelog("Shutting abuser with packet %u...\n", RFIFOW(me, 0));
		me->eof = 1;
	}
	*/
	
	int size = buffer_read(me->rbuffer, me->fd);
	if (size <= 0) {
			return 1;
	}
	else if (size + me->rbuffer->mark >= me->rbuffer->size-1) {
		buffer_realloc(me->rbuffer, 1024);
	}
	return 0;
}

int netPacketParser (struct client *me) {
	// It is called always we have data in rbuffer.
	if (me->packetnum != PacketUnknown && me->rbuffer->mark < me->packetlen) {
		printf("%d bytes missing from a %d packet.\n", me->packetlen, me->packetnum);
		return 0;
	}
	if (me->rbuffer->mark < 2 && me->packetnum == PacketUnknown) {
		if (++me->packetlife == 0) {
			amelog("Shutting a bogus client or laggy player.\n");
			return 0;
		}
		return 0;
	}
	else if (me->packetnum == PacketUnknown) {
		me->packetnum = PACKET_NUM(me->rbuffer->data);
		if (me->packetnum <0 || me->packetnum >= PacketUnknown) {
			amelog("Warning: Skipping unknown packet %d.\n", me->packetnum);
			me->packetlen = 2;
			netParserFinishPacket(me);
			return 0;
		}
		me->packetlen = NP[me->packetnum].length; //Set the default packetlen
		if (me->rbuffer->mark < me->packetlen)
			return 0;
	}
	int result = 0;
	result = NP[me->packetnum].proc(me);
	if (result == 0 && me->rbuffer->mark)
		netParserFinishPacket(me);
	else if (result < 0)
#ifdef _AME_DEBUG_
		netParserFinishPacket(me);
#else
		return 1;
#endif
	else {
		if (++me->packetlife == 0) {
			amelog("Shutting a bogus client or laggy player on packet %d.\n", me->packetnum);
			return 0;
		}
	}
	return 0;
}

int netParserClientChat(struct client *me) { // 32
	amelog("In netParserClientChat, %d bytes in packet, %d read.\n", me->packetlen, me->rbuffer->mark);
	if (me->rbuffer->mark == 2)
		return 1; // We need at least two bytes to be read...
	else if (me->packetlen > 2 && me->rbuffer->mark < me->packetlen) {
		return 1; //
	}
	me->packetlen = (unsigned)me->rbuffer->data[2];
	if (me->packetlen > me->rbuffer->mark)
		return 1;

	if ((unsigned)me->rbuffer->data[2] <= 3 || (unsigned)me->rbuffer->data[2] > 254) {
		WFIFOB(me, 0) = 0x09;
		WFIFOSP(me, 2);
		return 0;		
	}

	client_chats(me);
	return 0;
}

int netParserClientPMChat(struct client *me) {
	return 0;
}

int netParserClientPartyChat(struct client *me) {
	return 0;
}

int netParserClientGuildChat(struct client *me) {
	return 0;
}

int netParserClientLogin(struct client *me) {
	me->packetlen = RFIFOB(me, 2);
	if (me->packetlen < 3 || me->packetlen > 64) {
		return -1;
	}
	if (me->rbuffer->mark < me->packetlen)
		return 1;
	return client_login(me);
}

int netParserCharMovement(struct client *me) {
	if (me->achar == 0)
		return 0;
	if (me->achar->status.dead)
	{
		WFIFOPR(me, 8);
		WFIFOW(me, 0) = 20;
		WFIFOW(me, 2) = me->achar->id;
		WFIFOW(me, 4) = me->achar->cell->X;
		WFIFOW(me, 6) = me->achar->cell->Y;
		WFIFOSP(me, 8);
		client_writeln(me, "Remember you are dead? You cannot move :)");		
		return 0;
	}
	me->packetlen = RFIFOB(me, 2);
	if (me->rbuffer->mark < me->packetlen)
		return 1;
	if (me->packetlen > me->rbuffer->mark)
		return 1;

	int i, x, y;

	me->achar->status.action = CHA_WALKING;
	me->achar->status.nextAction = s.clock + (1000 / (me->achar->props.speed/50));

	if (!me->achar->steps) {
		me->achar->steps = chain_setInit(0,0,0);
	}
	else if (me->achar->steps->first) {
		chain_setClear(me->achar->steps);
	}
	for (i=3;i<me->packetlen-3;i+=4) {
		x = RFIFOW(me, i);
		y = RFIFOW(me, i+2);
		printf("%d:%d ", x, y);
		if (x >= 0 && x < me->achar->cell->wmap->width &&
				  y >= 0 && y < me->achar->cell->wmap->height) {
			client_pushMove(me, x, y);
			amelog("Adding a trajectory point at %d:%d\n", x, y);
		}
	}
	printf("\n");

/*
	
	if (me->achar->steps == 0)
		me->achar->steps = chain_setInit(0,0,255);
		
	struct walkpath_data wpd;
	wpd.result_chain = me->achar->steps;
	amelog("Calculating path data.\n");
	int res = path_search(&wpd, me->achar->cell->wmap->map_id, me->achar->cell->X, me->achar->cell->Y, x, y, 0x10001);
	printf("Cells in keycells: %d\n", wpd.result_chain->len);
	amelog("Pathdata calculated, res is %d, len is %d.\nPath data: \n", res, wpd.path_len);
	struct chain_node *node;
	for (node=wpd.result_chain->first;node;node=node->next) {
		printf("%d:%d ", ((struct world_cell *)node->val)->X, ((struct world_cell *)node->val)->Y);
	}
	printf("\n");
*/

	if (me->achar->steps->len) {
		struct chain_set *tmp = chain_setInit(0,0,255);
		if (map_foreach(WO_CHAR, me->achar->cell, CHAR_RADIUS, tmp)) {
			struct chain_node *node;
			for (node=tmp->first;node;node=node->next) {
				if (CHAR(node->val)->client && CHAR(node->val)->client != me) {
					WFIFOPR(CHAR(node->val)->client, 8);
					WFIFOW(CHAR(node->val)->client, 0) = 21;
					WFIFOW(CHAR(node->val)->client, 2) = me->achar->id;
					WFIFOW(CHAR(node->val)->client, 4) = ((struct world_cell *)me->achar->steps->last->val)->X;
					WFIFOW(CHAR(node->val)->client, 6) = ((struct world_cell *)me->achar->steps->last->val)->Y;
					WFIFOSP(CHAR(node->val)->client, 8);
				}
			}
		}
	}
	return 0;
}

int netParserMobLoot(struct client *me)
{
	int looted_id = RFIFOW(me, 2);
	amelog("Trying to get loot from mob id %d\n", looted_id);
	struct chain_set *mobs = CELL(me->achar->cell)->wmap->mobs;
	if (mobs && mobs->first)
	{
		struct chain_node *node = chain_findExactIndex(mobs, looted_id);
		if (!node)
			goto not_found;
		struct world_mob *mob = node->val;
		amelog("Mob found: %p\n",mob);
		if (!mob->status.dead)
			goto not_found;
		client_sendLootPacket(me, mob);
		return 0;
	}
	
	// Evetything falls down here
	not_found:
		WFIFOPR(me, 4);		
		WFIFOW(me, 0) = 72;
		WFIFOW(me, 2) = looted_id;
		WFIFOSP(me, 4);
	return 0;
}

int netParserCharselectCount(struct client *me) {
	if (!me || me->userid == 0)
		return -1;
	unsigned int namelen = 0;
	char tmp[255];
	memset(tmp, 0, 255);
	sprintf(tmp, "SELECT id, name, sex, class, lvl, exp, wspeed, aspeed FROM uchar WHERE user_id = %d LIMIT 9", me->userid);
	if (mysql_query(&s.mysql_h, tmp)) {
		amelog("Problem with query\n %s \n", tmp);
		mysql_error(&s.mysql_h);
		return 0;
	}
	s.sql_res = mysql_store_result(&s.mysql_h);
	printf("Characters found: %llu\n", mysql_num_rows(s.sql_res));
	if (s.sql_res && mysql_num_rows(s.sql_res)) {
		WFIFOW(me, 0) = 30;
		WFIFOB(me, 2) = mysql_num_rows(s.sql_res);
		WFIFOSP(me, 3);
		while ((s.sql_row = mysql_fetch_row(s.sql_res))) {
			namelen = strlen(s.sql_row[1]);
			amelog("Sending char packet, len is %d\n", namelen + 10);			
			WFIFOPR(me, 10 + namelen);
			WFIFOW(me, 0) = 31;
			WFIFOB(me, 2) = 10 + namelen;
			WFIFOW(me, 3) = 1000 + atoi(s.sql_row[0]);
			WFIFOB(me, 5) = atoi(s.sql_row[2]);
			WFIFOB(me, 6) = atoi(s.sql_row[3]);
			WFIFOB(me, 7) = atoi(s.sql_row[4]);
			WFIFOW(me, 8) = atoi(s.sql_row[5]);
			memcpy(WFIFOP(me, 10), s.sql_row[1], namelen);
			WFIFOSP(me, 10 + namelen);
		}
		mysql_free_result(s.sql_res);
	}
	else {
		WFIFOW(me, 0) = 30;
		WFIFOB(me, 2) = 0;
		WFIFOSP(me, 3);
	}
	return 0;
}

int netParserCharselectInfo(struct client *me) {
	return 0;
}

int netParserCharselect(struct client *me) {
	if (me->rbuffer->mark == 2)
		return 1;
	else if (me->achar != 0) {
		amelog("This user already is controlling a char!\n");
		return 0;
	}
	
	int id = RFIFOW(me, 2);
	
	amelog("Selecting the %d char.\n", id);
	sprintf(s.buffy, "SELECT id + 1000, name, sex, class, lvl, exp, wspeed FROM uchar WHERE id = %d-1000 AND user_id = %d LIMIT 1", id, me->userid);
	if (mysql_query(&s.mysql_h, s.buffy)) {
		amelog("Problem with query\n %s \n", s.buffy);
		mysql_error(&s.mysql_h);
		return 0;
	}
	s.sql_res = mysql_store_result(&s.mysql_h);
	if (s.sql_res && mysql_num_rows(s.sql_res)) {
		s.sql_row = mysql_fetch_row(s.sql_res);

		WFIFOPR(me, 2);
		WFIFOW(me, 0) = 32;
		WFIFOSP(me, 2);

		client_enterMap(me, 0, 25, 25);
	}
	else {
		WFIFOPR(me, 2);
		WFIFOW(me, me->wbuffer->mark) = 33;
		WFIFOSP(me, 2);
	}
	mysql_free_result(s.sql_res);
	return 0;
}

int netParserSkipPacket(struct client *me) {
	return 0;
}

int netParserLookAroundReq (struct client *me) {
	/* Look around */
	int mobcount = 0;
	if (!me || !me->achar)
			return 0;
	 struct chain_set *set = chain_setInit(0, 0, 0);
	 if ( map_foreach(WO_CHAR | WO_MOB | WO_NPC, me->achar->cell, 8, set) ) {
		 struct chain_node *noderator;
		 for (noderator=set->first;noderator;noderator=noderator->next) {
			 if (noderator->index == WO_CHAR) {
			 	struct client *c = ((struct world_char *)noderator->val)->client;
					if (c == me)
						continue;
					if (!c->eof && c->achar != 0 && c->achar->cell != 0) {
						/* Clients see each other */
						char_sees(me->achar, WO_CHAR, c->achar);
						// char_sees(c->achar, WO_CHAR, me->achar);
					}
					else {
						amelog("Warning: something wrong in the list %p\n", c);
					}
			 }
			 else if (noderator->index == WO_MOB) {
				 amelog("XXX: Introducing Mob %d\n", ++mobcount);
				 struct world_mob *m = noderator->val;
				 WFIFOPR(me, 8);
				 WFIFOW(me, 0) = 20;
				 WFIFOW(me, 2) = m->id;
				 WFIFOW(me, 4) = m->cell->X;
				 WFIFOW(me, 6) = m->cell->Y;
				 WFIFOSP(me, 8);
				 mob_sees_char(((struct world_mob *)noderator->val), me->achar);
			 }
			 else if (noderator->index == WO_NPC){
				 struct world_npc *npc = noderator->val;
				 amelog("XXX: Introducing NPC %d\n", npc->id);
				 WFIFOPR(me, 8);
				 WFIFOW(me, 0) = 20;
				 WFIFOW(me, 2) = npc->id;
				 WFIFOW(me, 4) = npc->cell->X;
				 WFIFOW(me, 6) = npc->cell->Y;
				 WFIFOSP(me, 8);
			 }
		 }
	 }
	 chain_setShut(set);
	 return 0;
}

int netParserCharMapInfo(struct client *me) { return 0; }

int netParserFinishPacket(struct client *me) {
	if (me->rbuffer->mark == 0)
		return 0;

	if (me->rbuffer->mark >= me->packetlen) {
		if (buffer_move(me->rbuffer, me->packetlen)) {
			amelog("Failed to move buffer, closing client\n"	);
			me->eof = 1;
		}
	}
	me->packetlife = 0;
	me->packetnum = PacketUnknown;
	me->packetlen = 0;
	return 0;
}

int netParserInventoryList(struct client *me)
{
	amelog("Getting the inventory list (%u %u).\n", me->packetnum, me->packetlen);
	client_getInventory(me);
	return 0;
}

int netParserInventoryInfo(struct client *me)
{
	client_getInventoryInfo(me, RFIFOW(me, 2));
	return 0;
}

int netParserWhois (struct client *me) {
	amelog("In WHOIS\n");
	if (me->rbuffer->mark < 4)
		return 1;
	if (me->achar == 0 || me->achar->cell == 0 || me->achar->position == 0)
		return 0;
	int code = RFIFOW(me, 0);
	int requested_id = RFIFOW(me, 2);
	if (code == 23) {
		struct chain_node *node = chain_findIndex(s.chars, requested_id-1000);
		if (node->index == requested_id && node->val != 0) {
			WFIFOPR(me, 5+16);
			WFIFOW(me, 0) = 22;
			WFIFOB(me, 2) = 5+16;
			WFIFOW(me, 3) = requested_id;
			memcpy(WFIFOP(me, 5), CHAR(node->val)->name, 16);
			WFIFOSP(me, 5+16);
		}
	}
	else if (requested_id > 1000) {
		/* Global char lookup */
		amelog("In global lookup.\n");
		struct chain_node *node = chain_findIndex(s.chars, requested_id);
		if (node->index == requested_id && node->val != 0) {
			WFIFOPR(me, 9 + 16);
			WFIFOW(me, 0) = 23;
			WFIFOB(me, 2) = 9+16;
			WFIFOW(me, 3) = requested_id;
			WFIFOB(me, 5) = CHAR(node->val)->props.sex;
			WFIFOB(me, 6) = CHAR(node->val)->props.class_id;
			WFIFOB(me, 7) = (unsigned short)CHAR(node->val)->props.speed,
			WFIFOB(me, 8) = char_getStatus(CHAR(node->val)); 
			memcpy(WFIFOP(me, 9), ((struct world_char *)node->val)->name, 16);
			WFIFOSP(me, 9+16);
		}
		else {
			amelog("Char %d not found (first is %d and last is %d).\n", requested_id, s.clients->first->index, s.clients->last->index);
		}
	}
	else if (requested_id > 100) {
		/* Local mob or npc lookup */
		struct chain_node *node = chain_findIndex(me->achar->cell->wmap->mobs, requested_id);
		if (node && node->index == requested_id && node->val != 0) {
			amelog("Sending a node...\n");
			WFIFOPR(me, 9+16);
			WFIFOW(me, 0) = 24;
			WFIFOB(me, 2) = 9+16;
			WFIFOW(me, 3) = requested_id;
			WFIFOW(me, 5) =	MOB(node->val)->subid;
			WFIFOB(me, 7) = MOB(node->val)->stats.wspeed;
			WFIFOB(me, 8) = mob_getStatus(MOB(node->val));
			memcpy(WFIFOP(me, 9), MOB(node->val)->name, 16);
			WFIFOSP(me, 9+16);
		}
		else {
			amelog("Mob %d not found.\n", requested_id);
		}
	}
	else
	{
		/* Local lookup for npc */
		struct chain_node *node = chain_findIndex(me->achar->cell->wmap->npcs, requested_id);
		if (node && node->val)
		{
			WFIFOPR(me, 9+16);
			WFIFOW(me, 0) = 24;
			WFIFOB(me, 2) = 9+16;
			WFIFOW(me, 3) = requested_id;
			WFIFOW(me, 5) = NPC(node->val)->looklikeid;
			WFIFOB(me, 7) = 0;
			WFIFOB(me, 8) = NPC_STATUS;
			memcpy(WFIFOP(me, 9), NPC(node->val)->name, 16);
			WFIFOSP(me, 9+16);
		}
	}
	return 0;
}

int netParserLogout(struct client *me) {
	me->eof =1;
	return 0;
}

int netParserSkillPacket(struct client *me)
{
	int skillid = RFIFOB(me, 2);
	int level = RFIFOB(me, 3);
	int target = RFIFOW(me, 4);
	client_usesSkill(me, target, skillid, level);
	return 0;
}

int netParserMobLootGet(struct client *me)
{
	amelog("In netParserMobLootGet\n.");
	int mob_id = RFIFOW(me, 2);
	int pos = RFIFOB(me, 4);
	struct chain_node *node = chain_findExactIndex(me->achar->cell->wmap->mobs, mob_id);
	if (!node || !node->val)
	{
		amelog("Mob %d in netParserMobGet not found.\n", mob_id);
		WFIFOPR(me, 3);
		WFIFOW(me, 0) = 73;
		WFIFOB(me, 2) = pos;
		WFIFOSP(me, 3);
		return 0;
	}
	
	struct world_mob *mob = node->val; 
	node = mob->objects->first;
	
	while(pos && node)
		node = node->next,pos--;
	
	if (pos > 0 || !node->val)	
	{
		amelog("No loot found in %d, on position pos %d\n.", mob_id, RFIFOB(me, 4));
		amelog("Current position: %d\n", pos);
		WFIFOPR(me, 3);
		WFIFOW(me, 0) = 73;
		WFIFOB(me, 2) = pos;
		WFIFOSP(me, 3);
		return 0;
	}

	int item_id = node->index;
	int item_quantity = (int)node->val;
	sprintf(s.buffy,"INSERT INTO inventory(uchar_id, item_id, quantity) VALUES(%d, %d, %d)",
			me->achar->id-1000, item_id, item_quantity);
	if (mysql_query(&s.mysql_h, s.buffy))
	{
		int last_id = mysql_insert_id(&s.mysql_h);
		WFIFOPR(me, 4);
		WFIFOW(me, 0) = 74;
		WFIFOW(me, 2) = last_id; 
		WFIFOSP(me, 4);
	}
	node->index = 0;
	node->val = 0;
	return 0;

}
